use metal::*;

#[no_mangle]
pub extern "C" fn command_queue(layer: &MetalLayerRef) -> CommandQueue {
  let device = Device::system_default().unwrap();
  layer.set_device(&device);
  layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
  device.new_command_queue()
}

#[no_mangle]
pub extern "C" fn re_draw(
  layer: &MetalLayerRef,
  command_queue: &CommandQueueRef,
  pipeline_state: &RenderPipelineStateRef,
  v_buf: &BufferRef,
) {
  if let Some(drawable) = layer.next_drawable() {
    let pass_descriptor = RenderPassDescriptor::new();
    prepare_render_pass_descriptor(pass_descriptor, drawable.texture());
    let command_buffer = command_queue.new_command_buffer();
    let encoder = command_buffer.new_render_command_encoder(pass_descriptor);
    encoder.set_render_pipeline_state(pipeline_state);
    encoder.set_vertex_buffer(0, Some(v_buf), 0);
    encoder.draw_primitives(MTLPrimitiveType::Triangle, 0, 3);
    encoder.end_encoding();
    command_buffer.present_drawable(drawable);
    command_buffer.commit();
  }
}

#[no_mangle]
fn prepare_pipeline_state(
  device: &DeviceRef,
  vert: &FunctionRef,
  frag: &FunctionRef,
) -> RenderPipelineState {
  let pipeline_state_descriptor = RenderPipelineDescriptor::new();
  pipeline_state_descriptor.set_vertex_function(Some(vert));
  pipeline_state_descriptor.set_fragment_function(Some(frag));
  pipeline_state_descriptor
    .color_attachments()
    .object_at(0)
    .unwrap()
    .set_pixel_format(MTLPixelFormat::BGRA8Unorm);

  device
    .new_render_pipeline_state(&pipeline_state_descriptor)
    .unwrap()
}

fn prepare_render_pass_descriptor(descriptor: &RenderPassDescriptorRef, texture: &TextureRef) {
  let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
  color_attachment.set_texture(Some(texture));
  color_attachment.set_load_action(MTLLoadAction::Clear);
  color_attachment.set_clear_color(MTLClearColor::new(0.4, 0.2, 0.3, 1.0));
  color_attachment.set_store_action(MTLStoreAction::Store);
}
